package com.allwees.core.event.interceptor;

import com.allwees.core.common.parser.el.ExpressionInteractiveObject;
import com.allwees.core.common.parser.el.ExpressionParser;
import com.allwees.core.event.annotation.LocalEventAnnotationParser;
import com.allwees.core.event.annotation.LocalEventOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;

@Slf4j
public abstract class LocalEventSupport implements ApplicationContextAware, Serializable {

    private final LocalEventAnnotationParser annotationParser = new LocalEventAnnotationParser();
    protected ApplicationContext applicationContext;

    protected abstract ExpressionParser getParser();

    protected Object execute(LocalEventOperationInvoker invoker, Object target, Method method, Object[] args) {

        Collection<LocalEventOperation> ops = annotationParser.parseCacheAnnotations(method);

        Object result = invoker.invoke();

        ExpressionInteractiveObject interactiveObject = new ExpressionInteractiveObject(method, args, target);
        interactiveObject.setResult(result);

        for (LocalEventOperation o : ops) {
            if (o.getEvent() == null && !condition(interactiveObject, o)) {
                continue;
            }

            Object eventObj = newInstance(o.getEvent(), getParser().parse(o.getSource(), interactiveObject));

            if (eventObj instanceof ApplicationEvent) {
                applicationContext.publishEvent(eventObj);
            }
        }
        return result;
    }

    private boolean condition(ExpressionInteractiveObject interactiveObject, LocalEventOperation o) {

        ExpressionParser parser = getParser();

        if (!StringUtils.hasLength(o.getCondition())) {
            return true;
        }

        Object condition = parser.parse(o.getCondition(), interactiveObject);

        return condition != null && (Boolean) condition;
    }

    private Object newInstance(Class clz, Object source) {
        Object o = null;
        try {
            Class c = Class.forName(clz.getName());
            Constructor constructor = c.getConstructor(source.getClass());
            o = constructor.newInstance(source);
        } catch (Exception e) {
            log.error(">>>>>>>>>>>>>>>> event error", e);
        }
        return o;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}

